<?php

class URLTracker extends Request
{
    private static $RULE;
    private static $CURRENT;

    /**
     * set/get rule
     *
     * @param array $rule
     * @return mixed
     */
    public static function rule($rule = array()) {
        if (empty($rule)) {
            return self::$RULE;
        }

        self::$RULE = $rule;
    }

    /**
     * initialize
     *
     * @param array $rule
     * @return array
     */
    public static function init(array $rule) {
        self::rule($rule);
        $_vs = array();
        foreach (self::$RULE as $k => $v) {
            $_vs[$k] = is_array($v) ? array_extend($v, parent::Get($k, $v)) : parent::Get($k, $v);
        }
        self::$CURRENT = $_vs;
        return self::$CURRENT;
    }

    /**
     * parse query args and return query string
     *
     * @param array $params
     * @param bool $update
     * @return string
     */
    public static function parse($params = null, $update = false) {

        $C = self::$CURRENT;

        if (!empty($params)) {
            // parse query_string to array
            if (is_string($params)) {
                parse_str($params, $_Q);
                $params = $_Q;
            }
            $C = parse_param($C, $params);
            foreach ($C as $k => $v) {
                $C[$k] = self::reflex($v, self::$RULE[$k]);
            }
            if ($update) {
                self::$CURRENT = $C;
            }
        }

        return self::build_query($C);
    }

    /**
     * a single version of self::parse
     *
     * @param $key
     * @param $value
     * @param bool $update
     * @return string
     */
    public static function set($key, $value, $update = false) {
        return self::parse(array("$key" => $value), $update);
    }

    /**
     * drop query args and return query string
     *
     * @return string
     */
    public static function drop() {
        $args = func_get_args();
        $C = self::$CURRENT;

        if (count($args) > 0) {
            foreach ($args as $param) {
                if (array_key_exists($param, $C) && array_key_exists($param, self::$RULE)) {
                    $C[$param] = self::$RULE[$param];
                }
            }
        }

        return self::build_query($C);
    }

    /**
     * get current value by key
     *
     * @param $key
     * @return mixed
     */
    public static function val() {
        $args = func_get_args();
        $value = null;
        if (count($args)) {
            foreach ($args as $k => $v) {
                $args[$k] = is_int($v) ? $v : '\'' . $v . '\'';
            }
            $args = '[' . join('][', $args) . ']';
            @eval('$value = self::$CURRENT' . $args . ';');
        }
        return $value;
    }

    public static function form_val() {
        $args = func_get_args();
        $len = count($args);
        if ($len > 0) {
            if ($len == 1) {
                $name = $args[0];
            } else {
                $name = $args[0] . '[' . join('][', array_slice($args, 1)) . ']';
            }

            $value = call_user_func_array(array(self, 'val'), $args);

            if (!is_null($value)) {
                return '<input type="hidden" name="' . $name . '" value="' . $value . '"/>';
            }
        }
        return '';
    }

    /**
     * set current value to default, if it is exist
     */
    public static function del() {
        $args = func_get_args();
        $value = null;
        if (count($args)) {
            foreach ($args as $k => $v) {
                $args[$k] = is_int($v) ? $v : '\'' . $v . '\'';
            }
            $args = '[' . join('][', $args) . ']';
            @eval('$value = self::$CURRENT' . $args . ';');
            if (!is_null($value)) {
                @eval('self::$CURRENT' . $args . ' = self::$RULE' . $args . ';');
            }
        }
    }

    /**
     * deff
     *
     * @param $from
     * @param array $against
     * @return array
     */
    public static function diff($from, $against = array()) {
        $_n = array();
        foreach ($from as $k => $v) {
            if (!array_key_exists($k, $against)) {
                $_n[$k] = $v;
            } else {
                // they are all array
                if (is_array($v) && is_array($against[$k])) {
                    $_cmb = self::diff($v, $against[$k]);
                    if (!empty($_cmb)) {
                        $_n[$k] = $_cmb;
                    }
                } elseif ($v !== $against[$k]) {
                    $_n[$k] = $from[$k];
                }
            }
        }
        return $_n;
    }

    /**
     * convert types
     *
     * @param $value
     * @param string $default
     * @return array|float|int|string
     */
    private static function reflex($value, $default = '') {
        if (is_string($default)) {
            return (string)$value;
        }

        if (is_int($default)) {
            return (int)$value;
        }

        if (is_array($default)) {
            return (array)$value;
        }

        if (is_float($default)) {
            return (float)$value;
        }

        return $value;
    }

    public static function build_query($params = array()) {
        if (empty($params)) {
            $params = self::$CURRENT;
        }
        return http_build_query(self::diff($params, self::$RULE));
    }
}